/* Copyright 2006, 2007 Christophe Jeannin

    This file is part of xch.

    xch is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    xch is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with xch; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include "xch.h"


static xchutils utils;


int main(int argc, char* argv[])
{
	int c;

#if defined(G_OS_WIN32) && defined(NDEBUG)
	FreeConsole();
#endif

	gtk_init(&argc, &argv);
	startup_init();

	gtk_main();

	switch(utils.mode)
	{
	case XCH_ABORT:
	default:
		break;

	case XCH_LISTEN:
		c = listen_proc();
		if(c)
		{
			utils.tehsock = c;

			*(guint32*)(utils.buffer) = XCH_TEST;
			c = send(utils.tehsock, utils.buffer, 4, 0);

			if((c < 4) || (c == -1))
			{
				g_print("Error sending data.\n");
				break;
			}
			else
			{
				c = recv(utils.tehsock, utils.buffer, 2, 0);

				if((c < 2) || (c == -1) || (*(guint16*)(utils.buffer) != XCH_OK))
				{
					g_print("Error receiving data.\n");
				}
				else
				{
					main_init();
					gtk_main();
				}
			}
		}
		else
			g_print("Error.\n");

		break;

	case XCH_CONNECT:
		c = connect_proc();
		if(c)
		{
			g_print("Couldn't connect to server. Code : %d\n", c);
			break;
		}

		c = recv(utils.tehsock, utils.buffer, 4, 0);
		if((c < 4) || (c == -1) || (*(guint32*)(utils.buffer) != XCH_TEST))
		{
			g_print("Error receiving data.\n");
			break;
		}
		else
		{
			*(guint16*)(utils.buffer) = XCH_OK;

			c = send(utils.tehsock, utils.buffer, 2, 0);
			if((c < 2) || (c == -1))
			{
				g_print("Error sending data.\n");
			}
			else
			{
				main_init();
				gtk_main();
			}
		}
		break;
	}

#ifdef G_OS_WIN32
	WSACleanup();
#endif

	return 0;
}


void destroy_mainw(GtkWidget *widget, gpointer data)
{
	*(guint32*)(utils.buffer) = XCH_QUIT;
	send(utils.tehsock, utils.buffer, 4, 0);
	gtk_main_quit();
}


void go_handler(GtkWidget *widget, gpointer data)
{
	if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(utils.radio)))
	{
		utils.mode = XCH_CONNECT;
		utils.ip = inet_addr(gtk_entry_get_text(GTK_ENTRY(utils.entry)));
	}
	else
	{
		utils.mode = XCH_LISTEN;
	}

	gtk_widget_destroy(utils.startup);
}


void send_handler(GtkWidget *widget, gpointer data)
{
	GtkTextIter start;
	GtkTextIter end;
	char* txt;
	int s;

	gtk_text_buffer_get_bounds(utils.txtbuf, &start, &end);
	txt = gtk_text_buffer_get_text(utils.txtbuf, &start, &end, FALSE);
	s = send(utils.tehsock, txt, strlen(txt)+1, 0);
	g_free(txt);
}


gboolean socket_handler(gpointer data)
{
	int retval;
	struct timeval timeout;
	fd_set rsock;
	timeout.tv_sec = 0;
	timeout.tv_usec = 100;

	FD_ZERO(&rsock);
	FD_SET(utils.tehsock, &rsock);
	retval = select(utils.tehsock+1,&rsock,NULL,NULL,&timeout);
	if(retval > 0)
	{
		recv(utils.tehsock, utils.buffer, 512, 0);
		if(*(guint32*)(utils.buffer) == XCH_QUIT)
		{
			gtk_widget_destroy(utils.mainw);
			return FALSE;
		}
		gtk_text_buffer_set_text(utils.txtbuf, utils.buffer, -1);
	}
	else if(retval < 0)
	{
		g_print("Other peer disconnected.\n");
		gtk_widget_destroy(utils.mainw);
		return FALSE;
	}

	return TRUE;
}


void sock_init()
{
#ifdef G_OS_WIN32
	WSADATA osef;
	WSAStartup(MAKEWORD(1,1), &osef);
#endif

	utils.tehsock = socket(PF_INET, SOCK_STREAM, 0);
}


int connect_proc()
{
	struct sockaddr_in srvinfo;

	srvinfo.sin_family = AF_INET;
	srvinfo.sin_port = XCH_PORT;
	srvinfo.sin_addr.s_addr = utils.ip;

	return connect(utils.tehsock, (struct sockaddr*)&srvinfo, sizeof(srvinfo));	
}


int listen_proc()
{
	int s;
	struct sockaddr_in srvinfo;

	srvinfo.sin_family = AF_INET;
	srvinfo.sin_port = XCH_PORT;
	srvinfo.sin_addr.s_addr = 0;	/* 0.0.0.0 listen on all interfaces */

	bind(utils.tehsock, (struct sockaddr*)&srvinfo, sizeof(srvinfo));
	listen(utils.tehsock, 1);
	s = accept(utils.tehsock, NULL, 0);

#ifdef G_OS_WIN32
	closesocket(utils.tehsock);
#elif defined G_OS_UNIX
	shutdown(utils.tehsock, SHUT_RDWR);
#endif

	return s;
}


void startup_init()
{
	GtkWidget* frame;
	GtkWidget* go;
	GtkWidget* vbox1;
	GtkWidget* vbox2;
	GtkWidget* hbox;
	GtkWidget* gobox;

	sock_init();

	utils.mode = XCH_ABORT;
	utils.startup = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	gtk_window_set_title(GTK_WINDOW(utils.startup), "Start-up");
	gtk_container_set_border_width(GTK_CONTAINER(utils.startup), 10);

	vbox1 = gtk_vbox_new(FALSE, 10);

	frame = gtk_frame_new("Mode");
	vbox2 = gtk_vbox_new(FALSE, 0);
	gtk_container_set_border_width(GTK_CONTAINER(vbox2), 10);

	gtk_container_add(GTK_CONTAINER(frame), vbox2);

	utils.radio = gtk_radio_button_new_with_label(NULL, "Listen");
	gtk_box_pack_start(GTK_BOX(vbox2), utils.radio, FALSE, FALSE, 0);
	utils.radio = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(utils.radio), "Connect to :");
	hbox = gtk_hbox_new(FALSE, 10);
	gtk_box_pack_start(GTK_BOX(hbox), utils.radio, FALSE, FALSE, 0);
	utils.entry = gtk_entry_new ();
	gtk_widget_set_size_request(utils.entry, 95, -1);
	gtk_entry_set_max_length(GTK_ENTRY(utils.entry), 15);
	gtk_entry_set_text(GTK_ENTRY(utils.entry), XCH_DEF_SERVER);
	gtk_box_pack_start(GTK_BOX(hbox), utils.entry, FALSE, FALSE, 0);

	gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, FALSE, 0);

	gtk_box_pack_start(GTK_BOX(vbox1), frame, FALSE, FALSE, 0);

	go = gtk_button_new_from_stock(GTK_STOCK_OK);
	gobox = gtk_hbox_new(FALSE, 0);
	gtk_box_pack_start(GTK_BOX(gobox), go, TRUE, FALSE, 0);
	gtk_box_pack_start(GTK_BOX(vbox1), gobox, FALSE, FALSE, 0);

	gtk_container_add(GTK_CONTAINER(utils.startup), vbox1);

	g_signal_connect(G_OBJECT(utils.startup), "destroy", G_CALLBACK(gtk_main_quit), NULL);
	g_signal_connect(G_OBJECT(go), "clicked", G_CALLBACK(go_handler), NULL);

	gtk_widget_show_all(utils.startup);
}


void main_init()
{
	GtkWidget* vbox;
	GtkWidget* hbox;
	GtkWidget* send;

	utils.mainw = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	gtk_window_set_title(GTK_WINDOW(utils.mainw), "xch");
	gtk_widget_set_size_request(utils.mainw, 620, 120);
	vbox = gtk_vbox_new(FALSE, 0);
	hbox = gtk_hbox_new(FALSE, 0);

	utils.text = gtk_text_view_new();
	utils.txtbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(utils.text));

	send = gtk_button_new_with_label("Send");

	gtk_box_pack_start(GTK_BOX(hbox), send, TRUE, FALSE, 0);
	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
	gtk_box_pack_start(GTK_BOX(vbox), utils.text, TRUE, TRUE, 0);
	gtk_container_add(GTK_CONTAINER(utils.mainw), vbox);

	g_signal_connect(G_OBJECT(utils.mainw), "destroy", G_CALLBACK(destroy_mainw), NULL);
	g_signal_connect(G_OBJECT(send), "clicked", G_CALLBACK(send_handler), NULL);

	g_idle_add((GSourceFunc)socket_handler, NULL);

	gtk_widget_show_all(utils.mainw);
}
